using System.Collections.Generic;
using CNGraphi.U6URP;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;


namespace CNGraphi.U6URP
{
    [ExecuteInEditMode]
    [DisallowMultipleComponent]
    public class UI : BaseMeshEffect
    {
        const string ApplayShaderName = "CNGraphi/UI";

        #region 对外参数
        public float outlineSize = 0;
        [ColorUsage(true, true)]public Color outlineColor = Color.black;
        public Vector2 shadowDistance = Vector2.zero;
        public Color shadowColor = Color.black;
        [Range(0.001f, 1)] public float shadowAlphaThreshold = 0.3f;
        #endregion


        #region 内部属性
        // 材质
        private Material material = null;
        // 缓存顶点数据列表
        private List<UIVertex> m_vertexLst = new List<UIVertex>();
        // 阴影的UV偏移量
        private Vector2 m_ShadowDistance = Vector2.zero;
        #endregion


        // 材质可用性
        bool MaterialAdjust()
        {
            material = base.graphic.material;
            if (material == null) 
            {
                Debug.LogError("Material is null.");
                return false; 
            }
            if (material.shader == null)
            {
                Debug.LogError("Material's shader is missing or invalid.");
                return false;
            }
            if (material.shader.name != ApplayShaderName) 
            {
                Debug.LogError("Material's shader must be 'CNGraphi/UI'");
                return false; 
            }
            return true;
        }

        void UpdateMaterialData()
        {
            if(material == null) { return; }
            material.SetColor("_OutlineColor", outlineColor);
            material.SetFloat("_OutlineSize", outlineSize);
            material.SetVector("_ShadowDistance", m_ShadowDistance);
            material.SetColor("_ShadowColor", shadowColor);
            material.SetFloat("_ShadowAlphaThreshold", shadowAlphaThreshold);
        }
        void CloseMaterialShaderData()
        {
            if(material != null && material.shader != null && material.shader.name == ApplayShaderName)
            {
                material.SetFloat("_OutlineSize", 0);
                material.SetVector("_ShadowDistance", Vector2.zero);
            }
        }



        /// <summary>
        /// 唤醒时操作
        /// </summary>
        protected override void Awake()
        {
            base.Awake();

            if ( !MaterialAdjust() ) { return; }

            //判断当前UI摄像机中的渲染通道是否包含TexCoord1和TexCoord2，如果不包含，则添加
            var ch = base.graphic.canvas.additionalShaderChannels;
            var addch = AdditionalCanvasShaderChannels.TexCoord1;
            if ((ch & addch) != addch)
                base.graphic.canvas.additionalShaderChannels |= addch; //添加texcoord1通道
            addch = AdditionalCanvasShaderChannels.TexCoord2;
            if ((ch & addch) != addch)
                base.graphic.canvas.additionalShaderChannels |= addch; //添加texcoord2通道
        }



        /// <summary>
        /// 强制刷新网格定义及材质信息
        /// </summary>
        public void Refresh()
        {
            base.graphic.SetVerticesDirty();
        }



        /// <summary>
        /// 定义网格
        /// <para>非外部接口</para>
        /// </summary>
        /// <param name="vh"></param>
        public override void ModifyMesh(VertexHelper vh)
        {
            if (!IsActive()) { CloseMaterialShaderData();  return; }
            if (!MaterialAdjust()) { return; }

            //Debug.Log("ModifyMesh");

            //先获取当前的网格数据
            vh.GetUIVertexStream(m_vertexLst);

            //操作
            Operate();

            //清理并重新设置网格顶点三角面数据
            vh.Clear();
            vh.AddUIVertexTriangleStream(m_vertexLst);

            //透传参数
            UpdateMaterialData();
        }


        Vector2 realShadowDistance
        {
            get
            {
                var d = shadowDistance;
                d.y *= -1;
                return d * 10;
            }
        }


        //重新计算网格顶点及UV
        private void Operate()
        {
            for (int i = 0, count = m_vertexLst.Count - 3; i <= count; i += 3)
            {
                var v1 = m_vertexLst[i];
                var v2 = m_vertexLst[i + 1];
                var v3 = m_vertexLst[i + 2];

                // 计算三个点组成三角形的中心点
                var minX = _Min(v1.position.x, v2.position.x, v3.position.x);
                var minY = _Min(v1.position.y, v2.position.y, v3.position.y);
                var maxX = _Max(v1.position.x, v2.position.x, v3.position.x);
                var maxY = _Max(v1.position.y, v2.position.y, v3.position.y);
                var posCenter = new Vector2(minX + maxX, minY + maxY) * 0.5f;

                // 计算原始UV框（范围）
                var uvMin = _Min(v1.uv0, v2.uv0, v3.uv0);
                var uvMax = _Max(v1.uv0, v2.uv0, v3.uv0);
                var uvOrigin = new Vector4(uvMin.x, uvMin.y, uvMax.x, uvMax.y);

                // 计算原始顶点坐标和UV的方向
                Vector2 triX, triY, uvX, uvY;
                Vector2 pos1 = v1.position;
                Vector2 pos2 = v2.position;
                Vector2 pos3 = v3.position;
                if (Mathf.Abs(Vector2.Dot((pos2 - pos1).normalized, Vector2.right)) > Mathf.Abs(Vector2.Dot((pos3 - pos2).normalized, Vector2.right)))
                {
                    triX = pos2 - pos1;
                    triY = pos3 - pos2;
                    uvX = v2.uv0 - v1.uv0;
                    uvY = v3.uv0 - v2.uv0;
                }
                else
                {
                    triX = pos3 - pos2;
                    triY = pos2 - pos1;
                    uvX = v3.uv0 - v2.uv0;
                    uvY = v2.uv0 - v1.uv0;
                }

                // 为每个顶点设置新的Position和UV，并传入原始UV框
                v1 = SetNewPosAndUV(v1, outlineSize, posCenter, triX, triY, uvX, uvY, uvOrigin, realShadowDistance, ref m_ShadowDistance);
                v2 = SetNewPosAndUV(v2, outlineSize, posCenter, triX, triY, uvX, uvY, uvOrigin, realShadowDistance, ref m_ShadowDistance);
                v3 = SetNewPosAndUV(v3, outlineSize, posCenter, triX, triY, uvX, uvY, uvOrigin, realShadowDistance, ref m_ShadowDistance);

                // 应用设置后的UIVertex
                m_vertexLst[i] = v1;
                m_vertexLst[i + 1] = v2;
                m_vertexLst[i + 2] = v3;
            }
        }


        static private UIVertex SetNewPosAndUV
        (
            UIVertex pVertex,
            float LineWidth,
            Vector2 pPosCenter,
            Vector2 pTriangleX,
            Vector2 pTriangleY,
            Vector2 pUVX,
            Vector2 pUVY,
            Vector4 pUVOrigin,
            Vector2 shadowdist,
            ref Vector2 shadowUVDist
        )
        {
            // 顶点新的位置
            Vector3 pos = pVertex.position;

            #region 外描边距离
            float posXOffset = pos.x > pPosCenter.x ? LineWidth : -LineWidth; //顶点x轴位置在中心点左侧，则减去外扩尺寸
            float posYOffset = pos.y > pPosCenter.y ? LineWidth : -LineWidth; //顶点y轴位置在中心点右侧，则加上外扩尺寸
            #endregion

            #region 阴影的偏移距离
            if ((pos.x > pPosCenter.x && shadowdist.x > 0) || (pos.x < pPosCenter.x && shadowdist.x < 0))
                posXOffset += shadowdist.x;
            if ((pos.y > pPosCenter.y && shadowdist.y > 0) || (pos.y < pPosCenter.y && shadowdist.y < 0))
                posYOffset += shadowdist.y;
            #endregion

            pos.x += posXOffset;
            pos.y += posYOffset;
            pVertex.position = pos;

            // 顶点新的UV
            Vector4 uv = pVertex.uv0;
            Vector2 v2 = (pUVX / pTriangleX.magnitude * posXOffset/*x轴增量*/) * (Vector2.Dot(pTriangleX, Vector2.right) > 0 ? 1 : -1 /*x轴增量方向*/);
            uv.x += v2.x;
            uv.y += v2.y;
            v2 = (pUVY / pTriangleY.magnitude * posYOffset/*y轴增量*/) * (Vector2.Dot(pTriangleY, Vector2.up) > 0 ? 1 : -1/*y轴增量方向*/);
            uv.x += v2.x;
            uv.y += v2.y;
            pVertex.uv0 = uv;

            // 阴影的UV偏移量
            Vector2 sUV = pUVX / pTriangleX.magnitude * shadowdist.x * (Vector2.Dot(pTriangleX, Vector2.right) > 0 ? 1 : -1);
            sUV += pUVY / pTriangleY.magnitude * shadowdist.y * (Vector2.Dot(pTriangleY, Vector2.up) > 0 ? 1 : -1);
            shadowUVDist.x = sUV.x;
            shadowUVDist.y = sUV.y;

            // 记录原始UV范围
            pVertex.uv1 = new Vector2(pUVOrigin.x, pUVOrigin.y);
            pVertex.uv2 = new Vector2(pUVOrigin.z, pUVOrigin.w);

            return pVertex;
        }

        static private float _Min(float pA, float pB, float pC)
        { return Mathf.Min(Mathf.Min(pA, pB), pC); }

        static private float _Max(float pA, float pB, float pC)
        { return Mathf.Max(Mathf.Max(pA, pB), pC); }

        static private Vector2 _Min(Vector2 pA, Vector2 pB, Vector2 pC)
        { return new Vector2(_Min(pA.x, pB.x, pC.x), _Min(pA.y, pB.y, pC.y)); }

        static private Vector2 _Max(Vector2 pA, Vector2 pB, Vector2 pC)
        { return new Vector2(_Max(pA.x, pB.x, pC.x), _Max(pA.y, pB.y, pC.y)); }

    }
}


#if UNITY_EDITOR

[CustomEditor(typeof(UI))]
internal class UIInspectorEditor : UnityEditor.Editor
{
    SerializedProperty outlineSize;
    SerializedProperty outlineColor;
    SerializedProperty shadowDistance;
    SerializedProperty shadowColor;
    SerializedProperty shadowAlphaThreshold;

    private void OnEnable()
    {
        outlineSize = serializedObject.FindProperty("outlineSize");
        outlineColor = serializedObject.FindProperty("outlineColor");
        shadowDistance = serializedObject.FindProperty("shadowDistance");
        shadowColor = serializedObject.FindProperty("shadowColor");
        shadowAlphaThreshold = serializedObject.FindProperty("shadowAlphaThreshold");
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();

        EditorGUILayout.Space(3);
        EditorGUILayout.LabelField($"<b><color=#ffcc00>* No support (simple-sprite mesh) mode.</color></b>", new GUIStyle("minilabel") { richText=true, fontSize = 11 });

        EditorGUILayout.Space(3);
        EditorGUILayout.LabelField("Outline");
        EditorGUI.indentLevel++;
        EditorGUILayout.PropertyField(outlineSize, new GUIContent("Size"));
        EditorGUILayout.PropertyField(outlineColor, new GUIContent("Color"));
        EditorGUI.indentLevel--;
        EditorGUILayout.Space(3);
        EditorGUILayout.LabelField("Shadow");
        EditorGUI.indentLevel++;
        EditorGUILayout.PropertyField(shadowDistance, new GUIContent("Distance"));
        EditorGUILayout.PropertyField(shadowColor, new GUIContent("Color"));
        EditorGUILayout.PropertyField(shadowAlphaThreshold, new GUIContent("Alpha Threshold"));
        EditorGUI.indentLevel--;

        
        EditorGUILayout.Space(5);
        serializedObject.ApplyModifiedProperties();
    }
}

#endif